/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.diagram.classdiagram.editpart;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.Label;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.Request;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.window.Window;
import org.eclipse.ui.PlatformUI;
import org.eclipse.uml2.uml.Parameter;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.UMLPackage;
import org.eclipse.uml2.uml.ValueSpecification;

import com.cea.papyrus.classdiagram.parsers.modelgenerator.PropertyGenerator;
import com.cea.papyrus.core.PapyrusPlugin;
import com.cea.papyrus.core.editor.ModelManager;
import com.cea.papyrus.core.editpart.GraphNodeWithSemanticEditPart;
import com.cea.papyrus.core.editpart.IUMLObjectEditPart;
import com.cea.papyrus.core.figures.LabelFigure;
import com.cea.papyrus.core.helper.ModelerHelper;
import com.cea.papyrus.core.listener.UmlElementListener;
import com.cea.papyrus.core.preference.ClassDiagramCommonPreferenceConstants;
import com.cea.papyrus.core.ui.dialogs.PropertyLabelEditorDialog;
import com.cea.papyrus.core.utils.Debug;
import com.cea.papyrus.diagram.classdiagram.editpart.OperationEditPart.UmlOperationListener;
import com.cea.papyrus.diagram.classdiagram.figures.PropertyFigure;
import com.cea.papyrus.diagram.classdiagram.preference.IClassDiagramCustomAppearence;
import com.cea.papyrus.diagraminterchange2.di2.Di2Factory;
import com.cea.papyrus.diagraminterchange2.di2.GraphNode;
import com.cea.papyrus.diagraminterchange2.di2.Uml1SemanticModelBridge;
import com.cea.papyrus.umlutils.ICustomAppearence;

/**
 * Control class for {@link Property}.
 */
public class PropertyEditPart extends GraphNodeWithSemanticEditPart {

	/**
	 * Creates a new PropertyEditPart 
	 * @param graphNode parent of the graph node controlled by this edit part
	 * @param modelManager manager in charge of models, graphic and domain
	 */
	public PropertyEditPart(GraphNode graphNode, ModelManager modelManager) {
		super(graphNode,modelManager);
		_adapter = new UmlPropertyListener(this);
	}

//	/*
//	* (non-Javadoc)
//	* @see com.cea.papyrus.core.common.editpart.GraphNodeEditPart#createEditPolicies()
//	*/
//	/**
//	* 
//	*/
//	@Override
//	protected void createEditPolicies(){
//	super.createEditPolicies();
//	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	protected IFigure createFigure() {
		return new PropertyFigure();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	protected Label getNameLabel() {
		return ((PropertyFigure)getFigure()).getNameLabel();
	}

	/**
	 * Returns the Property element associated to this controller.
	 * @return the property element associated to this controller.
	 */
	public Property getProperty(){
		return (Property)((Uml1SemanticModelBridge)(getGraphNode()).getSemanticModel()).getElement();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Property getUmlElement() {
		return (Property)((Uml1SemanticModelBridge)(getGraphNode()).getSemanticModel()).getElement();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	protected void refreshVisuals() {

		Dimension size = getGraphNode().getSize();
		Point p = getGraphNode().getPosition();
		Rectangle r = new Rectangle(p ,size);
		PropertyFigure f = (PropertyFigure)getFigure();
		IPreferenceStore store = PapyrusPlugin.getDefault().getPreferenceStore();	
		//add Listener on the figure
		f.setStatic(getUmlElement().isStatic());

		/** stereotypes handling */

		/** Stereotype presentation */
		String stereotypePresentation = null;
		if(getGraphNode().getSemanticModel() != null) {
			if(getGraphNode().getSemanticModel().getPresentation() != null) {
				stereotypePresentation = getGraphNode().getSemanticModel().getPresentation();
			} else {
				getGraphNode().getSemanticModel().setPresentation(ModelerHelper.TEXT_STEREOTYPE_PRESENTATION);
				stereotypePresentation = ModelerHelper.TEXT_STEREOTYPE_PRESENTATION;
			}
		}

		// Retrieve current class and figure
		org.eclipse.uml2.uml.Property currentProperty = getUmlElement();
		com.cea.papyrus.umlutils.Element currentElement = new com.cea.papyrus.umlutils.Element(getUmlElement());

		IFigure fig = getFigure();
		((PropertyFigure) fig).refreshColors(getGraphNode());
		((LabelFigure) fig).refreshPresentation(getPresentation(), currentProperty, getGraphNode());
		boolean hasIcon  = currentElement.hasIcon();
		boolean hasShape = currentElement.hasShape();

		String stereotypes;
		stereotypes = horizontalStereotypesToDisplay();
		f.refreshStereotypes(stereotypes, getPresentation(), hasIcon, hasShape);

		if((stereotypes!="")&& 
				((stereotypePresentation != null) && (stereotypePresentation.equals(ModelerHelper.TEXT_ICON_STEREOTYPE_PRESENTATION) || 
						stereotypePresentation.equals(ModelerHelper.TEXT_STEREOTYPE_PRESENTATION)))){
			//stereotypes=stereotypes.substring(0, stereotypes.length()-2);
			f.setStereotypes(stereotypes);//add stereotypes on the operation figure
		}
		else {
			f.setStereotypes(null);
		}


		com.cea.papyrus.umlutils.Property utilProperty = new com.cea.papyrus.umlutils.Property(this.getUmlElement());

		// default label
		int style = IClassDiagramCustomAppearence.DEFAULT_UML_PROPERTY;
		if (ClassDiagramCommonPreferenceConstants.getPreferencePropertyVisibilityAppearance(store)){
			style = style | ICustomAppearence.DISP_VISIBILITY;
		}
		if (ClassDiagramCommonPreferenceConstants.getPreferencePropertyIsDerivedAppearance(store)){
			style = style  | ICustomAppearence.DISP_DERIVE;
		}
		if (ClassDiagramCommonPreferenceConstants.getPreferencePropertyNameAppearance(store)){
			style = style  | ICustomAppearence.DISP_NAME;
		}
		if (ClassDiagramCommonPreferenceConstants.getPreferencePropertyTypeAppearance(store)){
			style = style  | ICustomAppearence.DISP_TYPE;
		}
		if (ClassDiagramCommonPreferenceConstants.getPreferencePropertyMultiplicityAppearance(store)){
			style = style  | ICustomAppearence.DISP_MULTIPLICITY;
		}
		if (ClassDiagramCommonPreferenceConstants.getPreferencePropertyDefaultValueAppearance(store)){
			style = style  | ICustomAppearence.DISP_DFLT_VALUE;
		}
		if (ClassDiagramCommonPreferenceConstants.getPreferencePropertyModifiersAppearance(store)){
			style = style  | ICustomAppearence.DISP_MOFIFIERS;
		}

		// get properties on graph node for property: custom display
		String value = getProperty(ICustomAppearence.CUSTOM_APPEARENCE_KEY);
		if(value == null) {
			com.cea.papyrus.diagraminterchange2.di2.Property prop = Di2Factory.eINSTANCE.createProperty();
			getGraphNode().getProperty().add(prop);
			prop.setKey(ICustomAppearence.CUSTOM_APPEARENCE_KEY);
			value = ""+style;
			prop.setValue(value);
		}
		if(value != null) {
			try {
				style = Integer.parseInt(value);
			} catch (NumberFormatException e) {
				Debug.log(e);
			}
		}

		String newText = utilProperty.getCustomLabel(style);


		// get the label for the figure depending on the presentation
//		String newText;
//		if(ModelerFactory.USER_DEFINED_PRESENTATION.equals(presentation)) {
//		newText = utilProperty.getShortLabel(true);
//		} else {
//		newText = utilProperty.getLabel(); 
//		}

		// set new text
		f.display(newText);

		((GraphicalEditPart) getParent()).setLayoutConstraint(
				this,
				f,
				r);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void activate() {
		if (isActive()) {
			return;
		}
		// start listening for changes in the model
		if (getUmlElement()!=null){
			((Notifier)getUmlElement()).eAdapters().add(_adapter);
			if (getUmlElement().getType()!=null){
				((Notifier)(getUmlElement().getType())).eAdapters().add(_adapter);
			}
			if (getUmlElement().getUpperValue()!=null){
				((Notifier)(getUmlElement().getUpperValue())).eAdapters().add(_adapter);
			}
			if (getUmlElement().getLowerValue()!=null){
				((Notifier)(getUmlElement().getLowerValue())).eAdapters().add(_adapter);
			}
			if (getUmlElement().getDefaultValue()!=null){
				addListenerToDefaultValue(getUmlElement().getDefaultValue());
			}
		}

		super.activate();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void deactivate() {
		if (!isActive()) {
			return;
		}
		// stop listening for changes in the model
		if (getUmlElement()!=null){
			((Notifier)getUmlElement()).eAdapters().remove(_adapter);
			if (getUmlElement().getType()!=null){
				((Notifier)(getUmlElement().getType())).eAdapters().remove(_adapter);
			}
			if (getUmlElement().getUpperValue()!=null){
				((Notifier)(getUmlElement().getUpperValue())).eAdapters().remove(_adapter);
			}
			if (getUmlElement().getLowerValue()!=null){
				((Notifier)(getUmlElement().getLowerValue())).eAdapters().remove(_adapter);
			}
			if (getUmlElement().getDefaultValue()!=null){
				removeListenerFromDefaultValue(getUmlElement().getDefaultValue());
			}
		}

		super.deactivate();
	}

	/**
	 * Adds the operation edit part listener to the specified value specification 
	 * @param specification the specification to listen
	 */
	protected void addListenerToDefaultValue(ValueSpecification specification) {
		if(specification != null) {
			((Notifier)specification).eAdapters().add(_adapter);
		}
	}

	/**
	 * Removes the operation edit part listener from the specified value specification 
	 * @param specification the specification not to listen anymore
	 */
	protected void removeListenerFromDefaultValue(ValueSpecification specification) {
		if(specification != null) {
			specification.eAdapters().remove(_adapter);
		}
	}


//	/**
//	* 
//	* @return 
//	*/
//	protected PropertyFigure getPropertyFigure(){
//	return (PropertyFigure)createFigure() ;
//	}

//	/* (non-Javadoc)
//	* @see com.ibm.itso.sal330r.gefdemo.edit.NetworkElementEditPart#getPropertySource()
//	*/
//	/**
//	* 
//	* 
//	* @return 
//	*/
//	@Override
//	protected IPropertySource getPropertySource() {
//	if( propertySource == null ) {
//	propertySource = new EObjectPropertySource( this.getProperty());
//	}
//	return propertySource;

//	}

//	/**
//	* Init the labels Manager and register label controlers.<p>
//	* This method should be called when a Direct Edit request is performed.
//	*/
//	protected void initLabelControlerManager() {
//	if( labelsMngr != null ) {
//	return;
//	}

//	// Create and init it.
//	labelsMngr = new LabelControlerManager();
//	labelsMngr.createLabelControler( ((PropertyFigure)getFigure()), 
//	new PropertyAccessor(){
//	public String getValue() { return getProperty().getName(); }
//	public void setValue(String value) {
//	getProperty().setName(value); 
//	}
//	});
//	}

	/**
	 * the direct edit mechanism requires the edit part to handle the direct edit
	 * request. This method determines which label the user clicked on. The label
	 * is passed to the ConditionessionCellEditorLocator which positions the cell editor
	 * over the selected label.
	 * @param req 
	 */
	public void performDirectEdit(Request req) {
		com.cea.papyrus.umlutils.Property utilProperty = new com.cea.papyrus.umlutils.Property(getProperty());
		PropertyLabelEditorDialog dialog = new PropertyLabelEditorDialog(
				PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
				getProperty(),
				utilProperty.getLabel()
		);
		final int code = dialog.open();

		if (code == Window.OK) {
			PropertyGenerator generator = new PropertyGenerator(getProperty());
			generator.parseAndModifyProperty(dialog.getValue());
		}
	}

//	/* (non-Javadoc)
//	* @see org.eclipse.gef.editparts.AbstractEditPart#performRequest(org.eclipse.gef.Request)
//	*/
//	/**
//	* 
//	* 
//	* @param request 
//	*/
//	@Override
//	public void performRequest(Request request){
//	if (request.getType() == RequestConstants.REQ_DIRECT_EDIT) {
//	performDirectEdit(request);
//	}
//	}

	/**
	 * Returns the presentation of the node (custom label).
	 * @return the string corresponding to the representation of the node
	 */
	@Override
	public String getPresentation() {
		String presentation = getGraphNode().getSemanticModel().getPresentation();
		// default value = "". it is set to null by the tool
		if(presentation == null) {
			presentation = "";
			setPresentation(presentation);
		}
		return presentation;
	}

	/**
	 * Set the new presentation for the node, then refresh display.
	 * @param presentation the new presentation
	 */
	public void setPresentation(String presentation) {
		getGraphNode().getSemanticModel().setPresentation(presentation);
		refresh();
	}

	/**
	 * Listener specific for {@link PropertyEditPart}, as it needs to recalculate the label when defaultvalue is changed.
	 */
	public class UmlPropertyListener extends UmlElementListener {

		/**
		 * Creates a new UmlOperationListener
		 * @param editPart the edit part that listens for notifications
		 */
		public UmlPropertyListener(IUMLObjectEditPart editPart) {
			super(editPart);
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void notifyChanged(Notification notification) {
			// add the operation edit part listener to a parameter if it is created and added to the operation
			if(UMLPackage.PROPERTY__DEFAULT_VALUE == notification.getFeatureID(org.eclipse.uml2.uml.Property.class) ) {
				// should always be a parameter, as it is the owned parameter collection which is modified. No need to verify cast.
				switch(notification.getEventType()) {
				case Notification.SET:
					addListenerToDefaultValue((ValueSpecification)notification.getNewValue());
					break;
				case Notification.UNSET:
					removeListenerFromDefaultValue((ValueSpecification)notification.getOldValue());
					// specific case because of UMLDestroyElementListener. Does not listen to parameters destruction anymore...
					umlEditPart.refresh();
					break;
				default:
					break;
				} 
			}
			// does the same as super listener
			super.notifyChanged(notification);
		}

	}
}